#region Copyright
//       Copyright (c) 2009 - Antoine Jaussoin - http://www.jaussoin.com/blog/
// 
//        This program is free software: you can redistribute it and/or modify
//        it under the terms of the GNU General Public License as published by
//        the Free Software Foundation, either version 3 of the License, or
//        (at your option) any later version.
// 
//        This program is distributed in the hope that it will be useful,
//        but WITHOUT ANY WARRANTY; without even the implied warranty of
//        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//        GNU General Public License for more details.
// 
//        You should have received a copy of the GNU General Public License
//        along with this program.  If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using FT.Architecture.Entities;
using FT.Architecture.Example.Core.Entities.Cars;
using FT.Architecture.Example.Core.Entities.Pets;
using FT.Architecture.Example.Core.Entities.Subscriptions;

namespace FT.Architecture.Example.Core.Entities.Humans
{
    /// <summary>
    /// Human entity
    /// </summary>
    public abstract class HumanBase : Entity, IHuman
    {
        private IHuman marriedTo;
        private readonly ICollection<IHuman> children;
        private readonly Man father;
        private readonly Woman mother;
        private readonly ICollection<IPet> pets;
        private string cv;
        private readonly ICollection<Subscription> subscriptions;
        private readonly ICollection<ICar> cars;
        private readonly ICollection<IPet> borrowedPets;

        /// <summary>
        /// Constructor
        /// </summary>
        protected HumanBase()
        {
            children = new List<IHuman>();
            pets = new List<IPet>();
            subscriptions = new List<Subscription>();
            cars = new List<ICar>();
            borrowedPets = new List<IPet>();
            marriedTo = null;
            father = null;
            mother = null;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="father"></param>
        /// <param name="mother"></param>
        protected HumanBase(Man father, Woman mother):this()
        {
            this.father = father;
            this.mother = mother;
        }

        /// <summary>
        /// Name
        /// </summary>
        public virtual string Name { get; set; }

        /// <summary>
        /// Date Born
        /// </summary>
        public virtual DateTime DateBorn { get; set; }

        /// <summary>
        /// Produce a child
        /// </summary>
        /// <returns></returns>
        public virtual IHuman ProduceChild(string name, DateTime dateOfBirth, string telephoneNumber)
        {
            if (marriedTo != null)
            {
                IHuman child;

                if (this is Man)
                {
                    child = HumanFactory.GetRandomHuman((Man) this, (Woman) marriedTo);
                }
                else
                {
                    child = HumanFactory.GetRandomHuman((Man) marriedTo, (Woman) this);
                }

                child.Name = name;
                child.DateBorn = dateOfBirth;
                child.Telephone = telephoneNumber;

                children.Add(child);

                return child;
            }
            
            throw new Exception("You can't produce a child without being married. (yeah I know it's a bit old school)");
        }

        /// <summary>
        /// Buy a random pet
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual IPet BuyAPet(string name)
        {
            IPet newPet = PetFactory.CreateRandomPet(name);
            
            newPet.Owners.Add(this);
            Pets.Add(newPet);

            //If married, the wife/husband also owns the pet
            if (MarriedTo != null)
            {
                newPet.Owners.Add(MarriedTo);
                MarriedTo.Pets.Add(newPet);
            }

            return newPet;
        }

        /// <summary>
        /// Say something
        /// </summary>
        public abstract string SaySomething();

        /// <summary>
        /// Phone number
        /// </summary>
        public virtual string Telephone { get; set; }

        /// <summary>
        /// CV as a very long text
        /// </summary>
        public virtual string CV
        {
            get { return cv; }
            set { cv = value;}
        }

        /// <summary>
        /// Age (calculated from the <see cref="DateBorn"/>, not stored, not mapped)
        /// </summary>
        public virtual int Age
        {
            get
            {
                int years = DateTime.Now.Year - DateBorn.Year;
                DateTime birthday = DateBorn.AddYears(years);
                if (DateTime.Now.CompareTo(birthday) < 0)
                {
                    years--;
                }
                return years;
            }
        }

        /// <summary>
        /// <see cref="IHuman"/> to whom this <see cref="IHuman"/> is married to
        /// </summary>
        public virtual IHuman MarriedTo
        {
            get
            {
                return marriedTo;
            }
        }

        /// <summary>
        /// List of pets
        /// </summary>
        public virtual ICollection<IPet> Pets
        {
            get { return pets; }
        }

        public virtual ICollection<IPet> BorrowedPets
        {
            get { return borrowedPets; }
        }

        /// <summary>
        /// Subscriptions
        /// </summary>
        public virtual ICollection<Subscription> Subscriptions
        {
            get { return subscriptions; }
        }

        /// <summary>
        /// Cars
        /// </summary>
        public virtual ICollection<ICar> Cars
        {
            get { return cars; }
        }

        /// <summary>
        /// Father
        /// </summary>
        public virtual Man Father
        {
            get { return father; }
        }

        /// <summary>
        /// Mother
        /// </summary>
        public virtual Woman Mother
        {
            get { return mother; }
        }

        /// <summary>
        /// Mary
        /// </summary>
        /// <param name="human"></param>
        public virtual void Mary(IHuman human)
        {
            ((HumanBase) human).marriedTo = this;
            marriedTo = human;
        }

        /// <summary>
        /// List of children
        /// </summary>
        public virtual ICollection<IHuman> Children
        {
            get { return children; }
        }
    }
}